#pragma once

#include "stdafx.h"

namespace natrium {
	namespace util {
		class Named {
		public:
			Named(const std::string& name);
			Named(std::string&& name);
			virtual ~Named();

			const std::string& getName() const;

		private:
			std::string mName;
		};

		/*
		 * Quasi-singleton interface for uniquely named objects
		 *
		 * ex:
		 *
		 * struct Foo: public util::UniqueNamed {
		 *		Foo(): util::UniqueNamed("Bar") {}
		 * };
		 *
		 * Foo* fooPtr = util::getUnique<Foo>("Bar");
		 *
		 *
		 * [Rambling Note] -- initially I wanted to use shared_ptrs for storage, but
		 * shared_from_this has various issues when used either in constructors or destructors
		 * so you end up with either using raw pointers or enforced factory construction (which
		 * is silly for singleton-like objects)
		 */

		template <typename T>
		T* getUnique(const std::string& name); //throws an exception if the name is not found, or when the cast to type T cannot be done

		class UniqueNamed:
			public Named,
			public boost::noncopyable
		{
		public:
			template<typename T> 
			friend T* getUnique(const std::string& name);

			UniqueNamed(const std::string& name); //throws an exception if the name is already in use
			UniqueNamed(std::string&& name);
			virtual ~UniqueNamed();

		private:
			static std::map<std::string, UniqueNamed*> mNameRegistry;
		};

		template <typename T>
		T* getUnique(const std::string& name) {
			auto it = UniqueNamed::mNameRegistry.find(name);

			if (it == UniqueNamed::mNameRegistry.end())
				throw std::runtime_error("Failed to find unique name");

			T* result = dynamic_cast<T*>(it->second);
			if (result == nullptr)
				throw std::runtime_error("Cannot cast to the correct type");

			return result;
		}
	}
}